/*
	@file heap_test_gauss

	@brief Tests for heap by gauss

	@author gauss
*/

//todo: add logging
//		add test with "bad cases" checking
//		add some more performance tests
//		test realloc

#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<exception>
#include"../../api/heap.h"
#include"../../src/gauss/heap_gauss.h"

using namespace std;

#define _flags heap876::alloc_flags
#define _simple heap876::alloc_simple
#define _tagged heap876::alloc_tagged
#define _debug heap876::alloc_debug
#define _optimize_mem heap876::alloc_optimize_mem
#define _unsafe heap876::alloc_unsafe

//flags allocator will be created with
const _flags flags=(_flags)(_simple
	|_tagged
	/*|_debug*/);

//amount of memory for testing
const int total=1024*1024*70;

//parameters for simple2darray_test()
const int s2d_n=10;

//parameters for hardrandomarrays_test()
const int hr_num=5000,
	hr_steps=10,
	hr_size=30000;

//macro for test information output
#define Inf printf

/*
	This is simple test in which a 2-dimensional nxn array is allocated,
	filled (a[i,j]=i^2*j^2) and freed in the end.
*/
void simple2darray_test(int n,heap876 *hp)
{
	Inf("Simple test with 2-dimensional array\n");
	clock_t start=clock();//remember start time
	int i,j,**arr=(int**)hp->alloc(n*sizeof(int*));
	if(arr==NULL)
		return;
	for(i=n-1;i>=0;i--)//try to allocate arrays
		if((arr[i]=(int*)hp->alloc(n*sizeof(int)))==NULL)//if failed
		{
#ifndef _STUB//there is no free_all in stub mode
			if(flags&_tagged)
				hp->free_all(0);//it is faster if tags are supported
			else
#endif//_STUB
			{
				for(j=0;j<i;j++)
					hp->free(arr[j]);
				hp->free(arr);
			}
			return;
		}
	for(i=0;i<n;i++)//fill arrays with some simple data
		for(j=0;j<n;j++)
			arr[i][j]=i*j*i*j;
	for(i=0;i<n;i++)//output to be checked
	{
		for(j=0;j<n-1;j++)
			Inf("%d\t",arr[i][j]);
		Inf("%d",arr[i][n-1]);
		Inf("\n");
	}
#ifndef _STUB
	if(flags&_tagged)
		hp->free_all(0);//it is faster if tags are supported
	else
#endif//_STUB
	{
		for(i=0;i<n;i++)
			hp->free(arr[i]);
		hp->free(arr);
	}
	Inf("Time=%d ms\n",(clock()-start)*1000/CLOCKS_PER_SEC);//time of test
}

/*
	In this test several (arrays_num) random-sized (range 0..max_size) arrays
	are allocated at random order to make high fragmentation. Execution time and
	critical fragmentation are calculated. This all is repeated steps_num times.
*/
void hardrandomarrays_test(int arrays_num,int steps_num,int max_size,heap876 *hp)
{
	Inf("Hard test with random-ordered random-sized arrays\n");
	int **arrays=(int**)hp->alloc(arrays_num*sizeof(int*),1);
	if(arrays==NULL)
		return;
	bool *indexes=(bool*)hp->alloc(arrays_num*sizeof(bool),1);//indexes[i] means
		//that i-th array is already allocated
	if(indexes==NULL)
	{
		hp->free(arrays);
		return;
	}
	int i,j,already,step,whole=0;
	clock_t start;
	size_t sz;
	for(i=0;i<steps_num;i++)
	{
		for(j=0;j<arrays_num;j++)
		{
			arrays[j]=NULL;
			indexes[j]=false;
		}
		already=0;//number of already allocated arrays
		srand((unsigned)clock());
		Inf("step number %d started...\t",i+1);
		start=clock();//remember start time
		while(already<arrays_num)
		{
			if(!indexes[j=rand()%arrays_num])//select one of arrays(random)
			{
				indexes[j]=true;
				already++;
			}
			hp->free(arrays[j]);
			sz=rand()%max_size;
			arrays[j]=(int*)hp->alloc(sz);
#ifndef _STUB
			if((sz!=0)&&(arrays[j]==NULL))//if no space
			{
				Inf("Critical fragmentation: %d%%\n",(int)((double)
					(hp->get_occupancy())*100.0/(double)(hp->get_volume())));
				break;
			}
#endif//_STUB
		}
#ifndef _STUB//there is no free_all in stub mode
		if(flags&_tagged)
			hp->free_all(0);//it is faster if tags are supported
		else
#endif//_STUB
			for(j=0;j<arrays_num;j++)
				hp->free(arrays[j]);
		Inf("step finished, time=%d ms\n",
			step=(clock()-start)*1000/CLOCKS_PER_SEC);//time of step
		whole+=step;//add step time to whole test time
#ifndef _LOG
		getchar();
#endif//_LOG
	}
	hp->free(arrays);
	hp->free(indexes);
	Inf("Time=%d ms\n",whole);//time of whole test
}

/*
	Abstract test for any descendant of heap876
*/
int test(heap876 *hp)
{
	Inf("=========Test started=========\n");
	Inf("heap<%s> volume is %d\n",hp->get_name(),hp->get_volume());
#ifndef _LOG
	getchar();
#endif//_LOG
	simple2darray_test(s2d_n,hp);
#ifndef _LOG
	getchar();
#endif//_LOG
	hardrandomarrays_test(hr_num,hr_steps,hr_size,hp);
	Inf("==========Test ended==========\n");
	return(0);
}

/*
	Execution of abstract test above gauss's class.
*/
int main(int argc,char *argv[])
{
	heap_gauss *hp_gauss;
	try
	{
		hp_gauss=new heap_gauss(total,flags);
	}
	catch(exception e)
	{
		getchar();
		return(0);
	}
	int rc=test(hp_gauss);
#ifndef _LOG
	getchar();
#endif//_LOG
	return(rc);
}
